package stat;

import java.util.EnumMap;

import loadsave.LoadSaveable;
import loadsave.Memento;
import loadsave.loadsaveobject.Iterator;
import loadsave.loadsaveobject.LoadSaveMapComposite;
import loadsave.loadsaveobject.LoadSaveObject;
import loadsave.loadsaveobject.LoadSaveObjectComposite;
import loadsave.loadsaveobject.LoadSavePrimitive;
import model.ModelObject;

public abstract class StatList extends ModelObject implements LoadSaveable {

    private EnumMap<StatType, Stat> statMap;
    
    public StatList() {
    	super("stats_list");
        statMap = new EnumMap<StatType, Stat>(StatType.class);
    }
    
    /**
     * get the stat value of the given type
     * @param st
     * @return
     */
    public int getStat( StatType st ) {
        return statMap.get(st).getValue();
    }
    
    /**
     * get the actual stat object for the given type
     * @param st
     * @return
     */
    public Stat getStatObject( StatType st ) {
        return statMap.get(st);
    }
    
    /**
     * add the given stat to the list
     * @param stat
     */
    protected void addStatToList(Stat stat) {
        statMap.put(stat.getType(), stat);
        this.notifyObservers();
    }
    
    /**
     * increment the given stattype in the list
     * @param type
     */
    public void increment(StatType type) {
    	statMap.get(type).increment();
    	this.notifyObservers();
    }
    
    /**
     * decrement the given stattype in the list
     * @param type
     * @return
     */
    public boolean decrement(StatType type) {
    	boolean ret = statMap.get(type).decrement(); 
        this.notifyObservers();
        return (ret);
    }
    
    /**
     * add the differential to the stat with the given type
     * @param st
     * @param differential
     */
    public void update(StatType st, int differential) {
        statMap.get(st).update(differential);
        this.notifyObservers();
    }
    
    /**
     * add the differential to the stat with the given type
     * @param type
     * @param differential
     */
    public void addStat( StatType type, int differential ) {
        statMap.get(type).add(differential);
        this.notifyObservers();
    }
    
    /**
     * subtract the differential from the stat with the given type
     * @param type
     * @param differential
     */
    private void subtractStat(StatType type, Stat differential) {
    	statMap.get(type).subtract(differential);
    	this.notifyObservers();
    }
    
    /**
     * add all stats of the two lists together
     * 'this' determines which stats will be affected
     * @param differential
     */
    public void addStats(StatList differential) {
		for (StatType type : differential.statMap.keySet()) {
			this.addStat(type, differential.getStat(type));
		}
	}
    
    /**
     * subtract all stats of the two lists feom each other
     * 'this' determines which stats will be affected
     * @param differential
     */
    public void subtractStats(StatList differential) {
		for (StatType type : differential.statMap.keySet()) {
			this.subtractStat(type, differential.getStatObject(type));
		}
    }
    
    /**
     * compare the two stat lists together.
     * -1 if this has all larger stats, 1 otherwise
     * @param statList
     * @return either 1 or -1
     */
    public int compare(StatList statList) {
    	for(Stat stat : statList.statMap.values()) {
     		if((stat.getValue() - statMap.get(stat.getType()).getValue()) > 0)
    			return -1;
    	}
    	return 1;
    }
    
    @Override
    public Memento getMemento() {
        return new Memento() {

            @Override
            public LoadSaveObject getState() {
                LoadSaveObjectComposite toReturn = new LoadSaveObjectComposite("", "StatList");
                
                LoadSaveMapComposite map = new LoadSaveMapComposite("statMap", "HashMap", "StatType", "Stat");
                for(StatType st : statMap.keySet()) {
                    map.add( new LoadSavePrimitive("","",String.valueOf(st)), new LoadSavePrimitive("","",String.valueOf(statMap.get(st).getValue())) );
                }
                toReturn.add(map.getName(), map);
                
                return toReturn;
            }

            @Override
            public void restoreState(LoadSaveObject lso) {
                EnumMap<StatType, Integer> statVals = new EnumMap<StatType, Integer>(StatType.class);
                for (StatType s : statVals.keySet())
                	statVals.put(s, 0);
                Iterator<LoadSaveObject> i = lso.get("statMap").getIterator();
                for (i.init(); i.hasNext(); i.next()) {
                	statVals.put(StatType.valueOf(i.getCurrKey().getValue()), Integer.valueOf(i.getCurrVal().getValue()));
                }
                //Stage 1 value assignment
                statMap.get(StatType.EXP).setValue(statVals.get(StatType.EXP));
                statMap.get(StatType.STR).setValue(statVals.get(StatType.STR));
                statMap.get(StatType.AGI).setValue(statVals.get(StatType.AGI));
                statMap.get(StatType.INT).setValue(statVals.get(StatType.INT));
                statMap.get(StatType.HAR).setValue(statVals.get(StatType.HAR));
                statMap.get(StatType.MVT).setValue(statVals.get(StatType.MVT));
                statMap.get(StatType.VISIBILITY).setValue(statVals.get(StatType.VISIBILITY));
                //Stage 2 value assignment
                statMap.get(StatType.LEVEL).setValue(statVals.get(StatType.LEVEL));
                statMap.get(StatType.ARMORRATING).setValue(statVals.get(StatType.ARMORRATING));
                //Stage 3 value assignment
                statMap.get(StatType.OFFENSIVERATING).setValue(statVals.get(StatType.DEFENSIVERATING));
                statMap.get(StatType.DEFENSIVERATING).setValue(statVals.get(StatType.DEFENSIVERATING));
                statMap.get(StatType.MAXHP).setValue(statVals.get(StatType.MAXHP));
                statMap.get(StatType.MAXMP).setValue(statVals.get(StatType.MAXMP));
                statMap.get(StatType.STATPOINT).setValue(statVals.get(StatType.STATPOINT));
                //Stage 4 value assignment
                statMap.get(StatType.HP).setValue(statVals.get(StatType.HP));
                statMap.get(StatType.MP).setValue(statVals.get(StatType.MP));
                statMap.get(StatType.LIVES).setValue(statVals.get(StatType.LIVES));
            }
            
        };
    }
    
    public String toString() {
    	return statMap.toString();
    }
}
